﻿#define _CRT_SECURE_NO_WARNINGS 1

//2.	构造函数
//1. 函数名与类名相同
//2. ⽆返回值。(返回值啥都不需要给，也不需要写void，不要纠结，C++规定如此)
//3. 对象实例化时系统会⾃动调⽤对应的构造函数。
//4. 构造函数可以重载。
//5. 如果类中没有显式定义构造函数，则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数，⼀旦⽤⼾显式定义编译器将不再⽣成。

//6. ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数，都叫做默认构造函
//    数。但是这三个函数有且只有⼀个存在，不能同时存在。⽆参构造函数和全缺省构造函数虽然构成
//	函数重载，但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫
//	默认构造，实际上⽆参构造函数、全缺省构造函数也是默认构造，总结⼀下就是不传实参就可以调
//	⽤的构造就叫默认构造。

//7. 我们不写，编译器默认⽣成的构造，对内置类型成员变量的初始化没有要求，也就是说是是否初始
	//化是不确定的，看编译器。对于⾃定义类型成员变量，要求调⽤这个成员变量的默认构造函数初始
	//化。如果这个成员变量，没有默认构造函数，那么就会报错，我们要初始化这个成员变量，需要⽤
	//初始化列表才能解决，初始化列表，我们下个章节再细细讲解。


//大多数情况下，构造函数都需要我们自己去实现，少数情况类似于MyQueue且Stack有默认构造时，MyQueue自动生成的就可以用，应写尽写


#include<iostream>
using namespace std;
class Date
{
public:
	// 1.⽆参构造函数
	// 可以认为是默认构造
	//Date()
	//{
	//	_year = 1;
	//	_month = 1;
	//	_day = 1;
	//}



	// 2.带参构造函数
	//Date(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}


	// 3.全缺省构造函数
	// 可以认为是默认构造
	/*Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}*/



	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	int GetYear()
	{
		return _year;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	// 如果留下三个构造中的第⼆个带参构造，第⼀个和第三个注释掉
	// 编译报错：error C2512: “Date”: 没有合适的默认构造函数可⽤

	
	Date d1; // 调⽤默认构造函数
	d1.Print();
	//-858993460/-858993460/-858993460			---		默认构造输出结果


	//Date d2(2025, 1, 1); // 调⽤带参的构造函数
	//d2.Print();


	// 注意：如果通过⽆参构造函数创建对象时，对象后⾯不⽤跟括号，否则编译器⽆法区分这⾥是函数声明还是实例化对象
	// warning C4930: “Date d3(void)”: 未调⽤原型函数(是否是有意⽤变量定义的?)
	// Date d3();

	return 0;
}













//析构函数

//析构函数与构造函数功能相反，析构函数不是完成对对象本⾝的销毁，⽐如局部对象是存在栈帧的，
//函数结束栈帧销毁，他就释放了，不需要我们管，C++规定对象在销毁时会⾃动调⽤析构函数，完成对
//象中资源的清理释放⼯作。析构函数的功能类⽐我们之前Stack实现的Destroy功能，⽽像Date没有
//Destroy，其实就是没有资源需要释放，所以严格说Date是不需要析构函数的。
 

//析构函数的特点：
//1. 析构函数名是在类名前加上字符 ~。
//2. ⽆参数⽆返回值。(这⾥跟构造类似，也不需要加void）
//3. ⼀个类只能有⼀个析构函数。若未显式定义，系统会⾃动⽣成默认的析构函数。
//4. 对象⽣命周期结束时，系统会⾃动调⽤析构函数。
//5. 跟构造函数类似，我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理，⾃定类型成员会
	//调⽤他的析构函数。
//6. 还需要注意的是我们显⽰写析构函数，对于⾃定义类型成员也会调⽤他的析构，也就是说⾃定义类
	//型成员⽆论什么情况都会⾃动调⽤析构函数。
//7. 如果类中没有申请资源时，析构函数可以不写，直接使⽤编译器⽣成的默认析构函数，如Date；如
	//果默认⽣成的析构就可以⽤，也就不需要显⽰写析构，如MyQueue；但是有资源申请时，⼀定要
	//⾃⼰写析构，否则会造成资源泄漏，如Stack。
//8. ⼀个局部域的多个对象，C++规定后定义的先析构。

#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}

	//析构函数
	~Stack()
	{
		//cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public:
		//编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构，释放的Stack内部的资源
		
		//显⽰写析构，也会⾃动调⽤Stack的析构
	~MyQueue()
	{
		cout << "~MyQueue()" << endl;
	}
private:
	Stack pushst;
	Stack popst;
};
int main2()
{
	//MyQueue mq;
	//Stack st1;

	//后定义的先析构 
	//Stack st2;

	MyQueue mq;

	return 0;
 }






//5.1 运算符重载
//1. 当运算符被⽤于类类型的对象时，C++语⾔允许我们通过运算符重载的形式指定新的含义。C++规
//定类类型对象使⽤运算符时，必须转换成调⽤对应运算符重载，若没有对应的运算符重载，则会编
//译报错。
//2. 运算符重载是具有特殊名字的函数，他的名字是由operator和后⾯要定义的运算符共同构成。和其他
//函数⼀样，它也具有其返回类型和参数列表以及函数体。
//3. 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数，⼆元
//运算符有两个参数，⼆元运算符的左侧运算对象传给第⼀个参数，右侧运算对象传给第⼆个参数。
//4. 如果⼀个重载运算符函数是成员函数，则它的第⼀个运算对象默认传给隐式的this指针，因此运算
//符重载作为成员函数时，参数⽐运算对象少⼀个。
//5. 运算符重载以后，其优先级和结合性与对应的内置类型运算符保持⼀致。
//6. 不能通过连接语法中没有的符号来创建新的操作符：⽐如operator@。
//7. .* :: sizeof ? : .注意以上5个运算符不能重载。(选择题⾥⾯常考，⼤家要记⼀
//	下)
//8. 重载操作符⾄少有⼀个类类型参数，不能通过运算符重载改变内置类型对象的含义，如： int
//	operator+(int x, int y)
//9. ⼀个类需要重载哪些运算符，是看哪些运算符重载后有意义，⽐如Date类重载operator - 就有意
//	义，但是重载operator + 就没有意义。
//10. 重载++运算符时，有前置++和后置++，运算符重载函数名都是operator++，⽆法很好的区分。
//C++规定，后置++重载时，增加⼀个int形参，跟前置++构成函数重载，⽅便区分。
//11. 重载 << 和 >> 时，需要重载为全局函数，因为重载为成员函数，this指针默认抢占了第⼀个形参位
//置，第⼀个形参位置是左侧运算对象，调⽤时就变成了 对象 << cout，不符合使⽤习惯和可读性。
//重载为全局函数把ostream / istream放到第⼀个形参位置就可以了，第⼆个形参位置当类类型对
//象。 



#include<iostream>
using namespace std;

bool operator<(Date d1, Date d2)
{
	return true;
}

bool operator==(Date d1, Date d2)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}

int main1()
{
	Date x1(2024, 7, 10);
	Date x2(2024, 8, 10);
	//operator==(x1, x2);
	if (x1 == x2)
		cout << 1;
	else
		cout << 0;
	return 0;
}